package orm

import (
	"errors"
	"strings"

	"github.com/go-xorm/xorm"
)

type DAInterface interface {
	Init(*xorm.Engine, interface{})
}

// 初始化DA对象
func InitDA(da interface{}, model interface{}) error {
	mi, ok := model.(ModelInterface)
	if !ok {
		return errors.New("model没有实现ModelInterface")
	}

	engine, err := Engineer.Get(mi)
	if err != nil {
		return err
	}

	di, ok2 := da.(DAInterface)
	if !ok2 {
		return errors.New("da没有实现DAInterface")
	}

	di.Init(engine, model)
	return nil
}

type DA struct {
	*xorm.Session
	model     interface{}
	updateMap map[string]interface{}
	columns   [][2]string
	Engine    *xorm.Engine
	ModelList *ModelList
}

func (da *DA) Init(engine *xorm.Engine, model interface{}) {
	da.Engine = engine
	da.model = model
	da.Session = engine.NewSession()

	//自动关闭连接
	da.Session.IsAutoClose = true

	//读取Model字段信息，预留50个上限
	tb := da.Session.Engine.TableInfo(model)
	da.columns = make([][2]string, 0, 50)
	for _, v := range tb.Columns() {
		da.columns = append(da.columns, [2]string{v.FieldName, v.Name})
	}
}

//添加记录
func (da *DA) Insert(model interface{}) (int64, error) {
	return da.Session.Insert(model)
}

//重置Update方法的更新内容
func (da *DA) Reset() {
	if da.updateMap != nil {
		//清空更新内容
		for k, _ := range da.updateMap {
			delete(da.updateMap, k)
		}
	}
}

//添加Update方法的更新内容
func (da *DA) Set(fieldNames string, values ...interface{}) *DA {
	if da.updateMap == nil {
		da.updateMap = make(map[string]interface{})
	}

	keys := strings.Split(fieldNames, ",")
	if len(keys) != len(values) {
		panic("键值匹配错误")
	}

	var isExist bool
	for i, n := 0, len(keys); i < n; i++ {
		isExist = false

		for _, col := range da.columns {
			if strings.EqualFold(keys[i], col[0]) {
				da.updateMap[col[1]] = values[i]
				isExist = true
				break
			}
		}

		if !isExist {
			panic("不存在字段名：" + keys[i])
		}
	}

	return da
}

//更新记录，须先调用da.Set
func (da *DA) Update() (int64, error) {
	switch {
	case len(da.Session.Statement.WhereStr) == 0:
		return 0, errors.New("没有设置更新条件")
	case da.updateMap == nil || len(da.updateMap) == 0:
		return 0, errors.New("没有数据内容，请使用da.Set进行设置")
	}

	return da.Session.Table(da.model).Update(&da.updateMap)
}

//删除记录
func (da *DA) Delete(model interface{}) (int64, error) {
	return da.Session.Delete(model)
}

//查询单条记录
func (da *DA) Get(model interface{}) (bool, error) {
	return da.Session.Get(model)
}

//查询列表记录
func (da *DA) GetModelList() (*ModelList, error) {
	var err error
	switch {
	case da.ModelList == nil:
		err = errors.New("ModelList未初始化")
	case da.ModelList.Items == nil:
		err = errors.New("Items未设置")
	case len(da.Session.Statement.OrderStr) == 0:
		err = errors.New("没有设置Orderby条件")
	}
	if err != nil {
		return nil, err
	}

	//反射结构体
	//	objV := reflect.Indirect(reflect.ValueOf(da.ModelList.Items))
	//	if objV.Kind() != reflect.Slice {
	//		err = errors.New("Items必须是一个切片")
	//		return nil, err
	//	}
	//	model := reflect.New(objV.Type().Elem()).Interface()

	//克隆查询对象
	sessionCount := da.Session.Clone()

	//清空Orderby条件，在某些数据库count不能带orderby
	sessionCount.Statement.OrderStr = ""

	da.ModelList.Total, err = sessionCount.Count(da.model)
	if err != nil {
		return nil, err
	}

	if da.ModelList.Total > 0 {
		da.Session.Limit(da.GetModelListLimit())
		err = da.Session.Find(da.ModelList.Items)
		if err != nil {
			return nil, err
		}
	} else {
		da.GetModelListCount()
	}

	return da.ModelList, nil
}

func (da *DA) GetModelListLimit() (int, int) {
	da.GetModelListCount()
	start := (da.ModelList.PageIndex - 1) * da.ModelList.PageSize
	return da.ModelList.PageSize, start
}

func (da *DA) GetModelListCount() {
	//检查页面长度
	if da.ModelList.PageSize <= 0 {
		da.ModelList.PageSize = 10
	}

	//计算总页数
	cnt := int(da.ModelList.Total / int64(da.ModelList.PageSize))
	mod := int(da.ModelList.Total % int64(da.ModelList.PageSize))
	if mod > 0 {
		cnt += 1
	}

	//检查页面索引
	switch {
	case cnt == 0:
		da.ModelList.PageIndex = 1
		da.ModelList.Items = make([]int, 0)
	case da.ModelList.PageIndex > cnt:
		da.ModelList.PageIndex = cnt
	case da.ModelList.PageIndex <= 0:
		da.ModelList.PageIndex = 1
	}

	//设置页面总页数
	da.ModelList.PageCount = cnt
}

//同xorm.Engine.In
func (da *DA) In(column string, args ...interface{}) *DA {
	da.Session.In(column, args...)
	return da
}

//同xorm.Engine.Where
func (da *DA) Where(querystring string, args ...interface{}) *DA {
	da.Session.Where(querystring, args...)
	return da
}

//同xorm.Engine.And
func (da *DA) And(querystring string, args ...interface{}) *DA {
	da.Session.And(querystring, args...)
	return da
}

//同xorm.Engine.Or
func (da *DA) Or(querystring string, args ...interface{}) *DA {
	da.Session.Or(querystring, args...)
	return da
}

//同xorm.Engine.Desc
func (da *DA) Desc(colNames ...string) *DA {
	da.Session.Desc(colNames...)
	return da
}

//同xorm.Engine.Asc
func (da *DA) Asc(colNames ...string) *DA {
	da.Session.Asc(colNames...)
	return da
}

//同xorm.Engine.Cols
func (da *DA) Cols(colNames ...string) *DA {
	da.Session.Cols(colNames...)
	return da
}
